package im.composer.media.audio.stream;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;

/**
 * 
 * @NonThreadSafe
 *
 */
public class LinkedByteArrayInputStream extends InputStream {

	private final Queue<byte[]> queue = new LinkedList<byte[]>();
	private ByteArrayInputStream current = null;
	private boolean closed = false;

	@Override
	public int available() throws IOException {
		if (current == null || current.available() < 1) {
			byte[] b_arr = queue.poll();
			if (b_arr != null) {
				current = new ByteArrayInputStream(b_arr);
			} else {
				return closed ? -1 : 0;
			}
		}
		return current.available();
	}

	@Override
	public void close() throws IOException {
		closed = true;
	}

	@Override
	public int read() throws IOException {
		if (available() < 1) {
			return -1;
		}
		return current.read();
	}

	public int size() {
		if (closed) {
			return -1;
		}
		return queue.stream().map(o -> o.length).reduce((sum, count) -> sum + count).orElse(0) + (current == null ? 0 : current.available());
	}

	public boolean offer(byte[] e) {
		Objects.requireNonNull(e);
		if (e.length < 1) {
			return true;
		}
		return queue.offer(e);
	}

	public synchronized void clear() {
		current = new ByteArrayInputStream(new byte[] {});
		queue.clear();
	}

	public OutputStream getOutputStream() {
		return new OutputStream() {

			@Override
			public void write(int b) throws IOException {
				write(new byte[] { (byte) (b & 0xFF) });
			}

			@Override
			public void write(byte[] b) throws IOException {
				offer(b);
			}

			@Override
			public void write(byte[] b, int off, int len) throws IOException {
				if (off == 0 && len == b.length) {
					write(b);
				} else {
					if(off+len>b.length){
						throw new IOException();
					}
					byte[] a = new byte[len];
					System.arraycopy(b, off, a, 0, len);
					write(a);
				}
			}
		};
	}

}
